﻿using System;
using System.Collections.Generic;

namespace Mesuro.Common {
	public interface ISeriesTransform : ISeries, ICloneable {
		ISeries Source { get; set; }
	}
	public abstract class AbstractSeriesTransform : ISeriesTransform {

		private ISeries _source;
		public virtual ISeries Source {
			get { return _source; }
			set {
				ISeries oldValue = _source;
				_source = value;
				OnSourceChanged( oldValue, value );
			}
		}

		public virtual IChannelCollection Channels {
			get { return Source.Channels; }
		}
		
		public virtual string Name {
			get { return Source.Name; }
			set { Source.Name = value; }
		}

		public virtual int Dimensions { get { return (Source != null) ? Source.Dimensions : 0; } }

		public event NotifySeriesChangedEventHandler DataChanged;
		protected void RaiseDataChanged( NotifySeriesChangedEventArgs e ) {
			CalcLimits();
			if (DataChanged != null)
				DataChanged( this, e );
		}
		protected virtual void CalcLimits() {
			if (Source == null) return;
			Limits[] lims = new Limits[Dimensions];
			foreach (Vector v in this) {
				for (int i = 0; i < lims.Length; i++) {
					lims[i].Union( v[i] );
				}
			}
			for (int i = 0; i < lims.Length; i++) {
				Channels[i].Limits = lims[i];
			}
		}
		protected virtual void OnSourceChanged( ISeries oldValue, ISeries newValue ) {
			CalcLimits();
			RaiseDataChanged( new NotifySeriesChangedEventArgs( this, NotifySeriesChangedAction.ChannelsChanged ) );
		}

		public abstract System.Collections.Generic.IEnumerator<Vector> GetEnumerator();

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
			return GetEnumerator();
		}

		public virtual object Clone() {
			return MemberwiseClone();
		}
	}
	public abstract class IterativeSeriesTransform : AbstractSeriesTransform {

		public virtual object CreateIterationContext() { return null; }

		public abstract Vector Transform( Vector source, object context );

		public override IEnumerator<Vector> GetEnumerator() {
			object context = CreateIterationContext();
			try {
				if (Source == null) yield break;
				IEnumerator<Vector> enumr = Source.GetEnumerator();
				while (enumr.MoveNext())
					yield return Transform( enumr.Current, context );
			} finally {
				if (context is IDisposable)
					((IDisposable)context).Dispose();
			}
		}

	}

	public delegate Vector DelegateSeriesTransformer<T>( DelegateSeriesTransform<T> self, Vector source, T context );
	public class DelegateSeriesTransform<T> : AbstractSeriesTransform {
		public static DelegateSeriesTransform<T> Create( ISeries source, T initialContext, DelegateSeriesTransformer<T> transform ) {
			return new DelegateSeriesTransform<T>( initialContext, transform ) { Source = source };
		}
		public static DelegateSeriesTransform<T> Create( T initialContext, DelegateSeriesTransformer<T> transform ) {
			return new DelegateSeriesTransform<T>( initialContext, transform );
		}

		private DelegateSeriesTransform( T initialContext, DelegateSeriesTransformer<T> transform ) {
			this.InitialContext = initialContext;
			this.Transform = transform;
		}

		private int? _dimensions = null;
		public override int Dimensions {
			get {
				return _dimensions ?? base.Dimensions;
			}
		}

		public void OverrideDimensions( int? dims ) {
			_dimensions = dims;
		}

		private T InitialContext;
		private DelegateSeriesTransformer<T> Transform;

		public override IEnumerator<Vector> GetEnumerator() {
			T context = (InitialContext is ICloneable) ? (T)((ICloneable)InitialContext).Clone() : InitialContext;
			if (Source != null) {
				IEnumerator<Vector> enumr = Source.GetEnumerator();

				while (enumr.MoveNext())
					yield return Transform( this, enumr.Current, context );
			}
		}
	}
}
